#!/usr/bin/php -q
<?php
	/*
	 * This file is part of phpitter.
 	 * 
	 * phpitter is free software: you can redistribute it and/or modify
	 * it under the terms of the GNU General Public License as published by
	 * the Free Software Foundation, either version 3 of the License, or
	 * (at your option) any later version.
 	 * 
	 * phpitter is distributed in the hope that it will be useful,
	 * but WITHOUT ANY WARRANTY; without even the implied warranty of
	 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	 * GNU General Public License for more details.
 	 * 
	 * You should have received a copy of the GNU General Public License
	 * along with phpitter.  If not, see <http://www.gnu.org/licenses/>.
	 *
	 * Copyright 2008 Ryan Hadley
	 */

	$version = 'phpitter 0.12 Beta (svn)';

	error_reporting(E_ERROR);

	$baseDir = dirname(__FILE__).'/';
	require($baseDir.'phpTwitter.class.php');
	require($baseDir.'PhpitterPluggable.class.php');

	$savedUser = '';
	$savedPass = '';
	if (file_exists($baseDir.'/.pass.php')) {
		// .pass.php will set $savedUser and $savedPass
		require_once($baseDir.'.pass.php');
	}

	$tweetHistory = 40;

	$pluggable = new PhpitterPluggable();

	$dir = opendir($baseDir.'plugins');
	while ( ($file = readdir($dir)) !== false )
	{
		if ( is_file($baseDir.'plugins/' . $file) )
			if ( ! preg_match('|^\.|', $file) )
				$plugins[] = $file;
	}

	if ( is_array($plugins) )
	{
		sort($plugins);
		foreach ( $plugins as $plugin )
			include($baseDir.'plugins/' . $plugin);
	}

	$tweetsToShow = 'All';

	$phpitterIcon = GdkPixbuf::new_from_file($baseDir.'phpitter_logo.png');

	$loginWindow = new GtkWindow();
	$loginWindow->set_title('phpitter login');
	$loginWindow->connect_simple('destroy', array('Gtk', 'main_quit'));
	$loginWindow->set_icon($phpitterIcon);

	$phpitterWindow = new GtkWindow();
	$phpitterWindow->add_events(Gdk::VISIBILITY_NOTIFY_MASK);
	$phpitterWindow->set_title('phpitter');
	$phpitterWindow->connect('destroy', 'exitPhpitter');
	$phpitterWindow->connect('delete-event', 'dontDelete', $phpitterWindow);
	$phpitterWindow->connect('hide', 'phpitterHide');
	$phpitterWindow->connect('show', 'phpitterShow');
	$phpitterWindow->connect('window-state-event', 'phpitterWindowStateEvent');
	$phpitterWindow->connect('visibility-notify-event', 'visibleWindow');
	$phpitterWindow->set_default_size(300, 700);
	$phpitterWindow->set_icon($phpitterIcon);

	$statusIcon = new GtkStatusIcon();
	$statusIcon->set_from_file($baseDir.'phpitter_logo.png');
	$statusIcon->set_visible(true);
	$statusIcon->connect_simple('activate', 'showHidePhpitter', $phpitterWindow);
	$statusIcon->connect_simple('popup-menu', 'showTrayMenu');

        $scrolled_window = new GtkScrolledWindow();
        $scrolled_window->set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
        $scrolled_window->set_shadow_type(Gtk::SHADOW_IN);
	$scrolled_window->modify_bg(Gtk::STATE_NORMAL, GdkColor::parse('#ffffff'));

	$friendsTable = new GtkTable($tweetHistory * 3, 1);
	$friendsTable->modify_bg(Gtk::STATE_NORMAL, GdkColor::parse('#ffffff'));

	$scrolled_window->add_with_viewport($friendsTable);

	$updateWindow = new GtkTable(4, 2);
	$updateWindow->set_row_spacings(0);
	$updateWindow->set_col_spacings(0);
	$updateLabel = new GtkLabel();
	$updateLabel->set_alignment(0, 0);
	$updateLabel->locked = false;
	$updateBox = new GtkTextView();


	$updateBox->set_wrap_mode(Gtk::WRAP_WORD_CHAR);
	$updateBox->set_size_request(250,80);


	$updateBoxFrame = new GtkFrame();
	$updateBoxFrame->modify_bg(Gtk::STATE_NORMAL, GdkColor::parse('#000000'));
	$updateBoxFrame->add($updateBox);

	$refreshImage = GtkImage::new_from_file($baseDir . '/images/arrow_refresh.png');
	$refreshButton = new GtkButton();
	$refreshButton->add($refreshImage);
	//$refreshButton->set_property('has-tooltip', true);
	//$refreshButton->connect('query-tooltip', 'btnTooltip', 'Fetch Timeline');
	$refreshButton->connect_simple('clicked', 'checkForUpdates', $friendsTable, $pluggable, true);

	$sendImage = GtkImage::new_from_file($baseDir . '/images/script_go.png');
	$sendButton = new GtkButton();
	$sendButton->add($sendImage);
	//$sendButton->set_property('has-tooltip', true);
	//$sendButton->connect('query-tooltip', 'btnTooltip', 'Send Update');
	$sendButton->connect_simple('clicked', 'sendUpdate', $updateBox);

	//function btnTooltip($widget, $x, $y, $kbd, $tooltip, $text)
	//{
		//$tooltip->set_text($text);
//
		//return true;
	//}

	$buttonVBox = new GtkVBox();
	$nullBox = new GtkHBox();

	$buttonVBox->pack_start($refreshButton, false, false);
	$buttonVBox->pack_start($sendButton, false, false);
	$buttonVBox->pack_start($nullBox);

	$updateHBox = new GtkHBox();
	$updateHBox->pack_start($updateBoxFrame);
	$updateHBox->pack_start($buttonVBox, false, false);

	if ( class_exists(GtkSpell) )
		$updateSpell = new GtkSpell($updateBox);
	else
		$updateSpell = null;

	$updateBox->get_buffer()->connect_simple('changed', 'changedEntryBox', $updateBox, $updateSpell, $pluggable);

	$listOptions = new GtkComboBox();
	$listStore = new GtkListStore(64);
	$listStore->append(array('All'));
	$listStore->append(array('Replies'));
	$listStore->append(array('Directs'));
	$listOptions->set_model($listStore);
	$cellRenderer = new GtkCellRendererText();
	$listOptions->pack_start($cellRenderer);
	$listOptions->set_attributes($cellRenderer, 'text', 0);
	$listOptions->set_active(0);
	$listOptions->connect('changed', 'listOptionChanged', $friendsTable, $pluggable);

	$updateBox->connect('key-press-event', 'ifEnterSendUpdate');

	$lastStatusLabel = new GtkLabel();
	$listOptionsHBox = new GtkHBox();
	$listOptionsHBox->pack_start($listOptions, false, false);
	$listOptionsHBox->pack_start($lastStatusLabel);

	$updateWindow->attach($listOptionsHBox, 0, 2, 0, 1, Gtk::FILL);
	$updateWindow->attach($updateHBox, 0, 2, 1, 2);
	$updateWindow->attach($updateLabel, 0, 2, 2, 3);

	$phpitterMenuBar = new GtkMenuBar();
	$phpitterMenuItem = new GtkMenuItem('_Settings');
	$aboutMenuItem = new GtkMenuItem('_About');
	$phpitterMenuBar->append($phpitterMenuItem);
	$phpitterMenuBar->append($aboutMenuItem);
	$massTweetItem = new GtkMenuItem('_Mass Tweet');
	$filtersItem = new GtkMenuItem('_Groups');
	$quitItem = new GtkMenuItem('_Quit');
	$viewOnMapItem = new GtkMenuItem('_View on Map');
	$phpitterMenu = new GtkMenu();
	$phpitterMenu->append($massTweetItem);
	$phpitterMenu->append($viewOnMapItem);
	$phpitterMenu->append($filtersItem);
	$phpitterMenu->append($quitItem);
	$phpitterMenuItem->set_submenu($phpitterMenu);
	$aboutMenu = new GtkMenu();
	$licenseItem = new GtkMenuItem('_License');
	$versionItem = new GtkMenuItem('_Version');
	$aboutMenu->append($licenseItem);
	$aboutMenu->append($versionItem);
	$aboutMenuItem->set_submenu($aboutMenu);

	$licenseItem->connect_simple('activate', 'showInfoWindow', file_get_contents($baseDir.'COPYING'));
	$versionItem->connect_simple('activate', 'showInfoWindow', 'You are running: ' . $version);

	$quitItem->connect_simple('activate', 'exitPhpitter');
	$filtersItem->connect_simple('activate', 'showFiltersWindow');
	$massTweetItem->connect_simple('activate', 'showMassTweetWindow');
	$viewOnMapItem->connect_simple('activate', 'sendMapData');

	$phpitterMainTable  = new GtkTable(3,1);
	$phpitterMainTable->attach($phpitterMenuBar, 0, 1, 0, 1, Gtk::FILL, Gtk::FILL);
	$phpitterMainTable->attach($scrolled_window, 0, 1, 1, 2);
	$phpitterMainTable->attach($updateWindow, 0, 1, 2, 3, Gtk::FILL, Gtk::FILL);
	$phpitterMainTable->modify_bg(Gtk::STATE_NORMAL, GdkColor::parse('#ffffff'));
	$phpitterWindow->add($phpitterMainTable);

	$lblCredit   = new GtkLabel('Enter your twitter login');
	$lblUsername = new GtkLabel('_Username', true);

	$lblPassword = new GtkLabel('_Password', true);
	$txtUsername = new GtkEntry($savedUser);
	$txtPassword = new GtkEntry($savedPass);
	$txtPassword->set_visibility(false);
	$btnLogin    = new GtkButton('_Login');
	$btnCancel   = new GtkButton('_Cancel');
	$lblUsername->set_mnemonic_widget($txtUsername);
	$lblPassword->set_mnemonic_widget($txtPassword);
	$btnCancel->connect_simple('clicked', array($loginWindow, 'destroy'));
	$btnLogin->connect_simple('clicked', 'login', $loginWindow, $phpitterWindow, $friendsTable, $txtUsername, $txtPassword, $pluggable);
	$txtUsername->connect('key-press-event', 'ifEnterLogin', $loginWindow, $phpitterWindow, $friendsTable, $txtUsername, $txtPassword, $pluggable);
	$txtPassword->connect('key-press-event', 'ifEnterLogin', $loginWindow, $phpitterWindow, $friendsTable, $txtUsername, $txtPassword, $pluggable);
	$tbl = new GtkTable(3, 2);
	$tbl->attach($lblCredit, 0, 2, 0, 1);
	$tbl->attach($lblUsername, 0, 1, 1, 2);
	$tbl->attach($txtUsername, 1, 2, 1, 2);
	$tbl->attach($lblPassword, 0, 1, 2, 3);
	$tbl->attach($txtPassword, 1, 2, 2, 3);
	$bbox = new GtkHButtonBox();
	$bbox->set_layout(Gtk::BUTTONBOX_EDGE);
	$bbox->add($btnCancel);
	$bbox->add($btnLogin);
	$vbox = new GtkVBox();
	$vbox->pack_start($tbl);
	$vbox->pack_start($bbox);
	$loginWindow->add($vbox);
	$loginWindow->show_all();

	function showMassTweetWindow()
	{
		global $groups, $curGroupName, $phpitterIcon;

		if ( count($groups) == 0 )
		{
			showNewFilterWindow();
		} else {
			$massTweetWindow = new GtkWindow();
			$massTweetWindow->set_icon($phpitterIcon);
			$massTweetWindow->set_position(Gtk::WIN_POS_MOUSE);

			$listOptions = new GtkComboBox();
			$listStore = new GtkListStore(64);
			foreach ( $groups as $group => $val )
				$listStore->append(array($group));
			$listOptions->set_model($listStore);
			$cellRenderer = new GtkCellRendererText();
			$listOptions->pack_start($cellRenderer);
			$listOptions->set_attributes($cellRenderer, 'text', 0);
			$listOptions->set_active(0);

			$listLabel = new GtkLabel('Choose Group: ');

			$typeOptions = new GtkComboBox();
			$typeStore = new GtkListStore(64);
			$typeStore->append(array('Reply'));
			$typeStore->append(array('Direct'));
			$typeOptions->set_model($typeStore);
			$typeCellRenderer = new GtkCellRendererText();
			$typeOptions->pack_start($typeCellRenderer);
			$typeOptions->set_attributes($typeCellRenderer, 'text', 0);
			$typeOptions->set_active(0);

			$typeLabel = new GtkLabel('Choose Type: ');

			$updateBox = new GtkTextView();
			$updateBox->set_wrap_mode(Gtk::WRAP_WORD_CHAR);
			$updateBox->set_size_request(250,80);
			$updateBoxFrame = new GtkFrame();
			$updateBoxFrame->modify_bg(Gtk::STATE_NORMAL, GdkColor::parse('#000000'));
			$updateBoxFrame->add($updateBox);

			$btnSendUpdate = new GtkButton('_Send');
			$btnSendUpdate->connect_simple('clicked', 'sendMassUpdate', $listOptions, $typeOptions, $updateBox, $massTweetWindow);

			$tbl = new GtkTable(4, 2);
			$tbl->attach($listLabel, 0, 1, 0, 1);
			$tbl->attach($listOptions, 1, 2, 0, 1);
			$tbl->attach($typeLabel, 0, 1, 1, 2);
			$tbl->attach($typeOptions, 1, 2, 1, 2);
			$tbl->attach($updateBoxFrame, 0, 2, 2, 3);
			$tbl->attach($btnSendUpdate, 0, 2, 3, 4);


			$massTweetWindow->add($tbl);
			$massTweetWindow->show_all();
		}
	}

	function sendMassUpdate($groupOption, $typeOption, $updateBox, $massTweetWindow)
	{
		global $groups, $phpTwitter;
		$group = $groupOption->get_active_text();
		$type = $typeOption->get_active_text();
		$text = $updateBox->get_buffer()->get_text($updateBox->get_buffer()->get_start_iter(), $updateBox->get_buffer()->get_end_iter());

		foreach ( $groups[$group] as $key => $user )
		{
			if ( $key !== "type" )
			{
				if ( $type == 'Reply' )
				{
					$sendText = "@$user $text";
					$phpTwitter->sendUpdate($sendText);
				} else {
					$phpTwitter->sendDirect($user, $text);
				}
			}
		}

		$massTweetWindow->destroy();
	}

	function showFiltersWindow()
	{
		global $groups, $curGroupName, $phpitterIcon;

		if ( count($groups) == 0 )
		{
			showNewFilterWindow();
		} else {
			$filtersWindow = new GtkWindow();
			$filtersWindow->set_icon($phpitterIcon);
			$filtersWindow->set_position(Gtk::WIN_POS_MOUSE);

			$listOptions = new GtkComboBox();
			$listStore = new GtkListStore(64);
			$x = 0;
			foreach ( $groups as $group => $val )
			{
				if ( $curGroupName == '' )
					$curGroupName = $group;
				if ( $curGroupName == $group )
				{
					$activeNum = $x;
				}
				$listStore->append(array($group));
				$x++;
			}

			$curGroup = $groups[$curGroupName];

			$listOptions->set_model($listStore);
			$cellRenderer = new GtkCellRendererText();
			$listOptions->pack_start($cellRenderer);
			$listOptions->set_attributes($cellRenderer, 'text', 0);
			$listOptions->set_active($activeNum);
			$listOptions->connect_simple('changed', 'groupListChanged', $listOptions, $filtersWindow);

                        $includeCheckBox = new GtkCheckButton('Inclusive Filter Type');

			if ( $curGroup['type'] == 'include' )
			{
				$includeCheckBox->set_active(true);
			} else {
				$includeCheckBox->set_active(false);
			}

			$includeCheckBox->connect_simple('toggled', 'changeGroupType', $includeCheckBox, $curGroupName);

			$usersInGroup = new GtkVBox();
			foreach ( $curGroup as $key => $value )
			{
				if ( trim($key) != "type" )
				{
					$userHBox = new GtkHBox(true);
					$newLabel = new GtkLabel($value);
					$userDel = new GtkButton('Delete');
					$userDel->connect_simple('clicked', 'removeUserFromGroup', $value, $curGroupName, $userHBox);
					$userHBox->pack_start($newLabel);
					$userHBox->pack_start($userDel);
					$usersInGroup->pack_start($userHBox);
				}
			}

			$groupButtons = new GtkHBox();
			$userEntry = new GtkEntry();
			$userEntryLabel = new GtkLabel('User: ');
			$userEntryButton = new GtkButton('Add');
			$groupButtons->pack_start($userEntryLabel);
			$groupButtons->pack_start($userEntry);
			$groupButtons->pack_start($userEntryButton);
			$userEntryButton->connect_simple('clicked', 'addUserToGroup', $userEntry, $curGroupName, $usersInGroup);

			$listButtonsBox = new GtkHBox();
			$deleteList = new GtkButton('Delete Group');
			$deleteList->connect_simple('clicked', 'delListClicked', $curGroupName, $filtersWindow);
			$newList = new GtkButton('New Group');
			$newList->connect_simple('clicked', 'newListClicked', $filtersWindow);
			$listButtonsBox->pack_start($deleteList);
			$listButtonsBox->pack_start($newList);

			$tbl = new GtkTable(4, 2);
			$tbl->attach($listButtonsBox, 0, 2, 0, 1);
			$tbl->attach($listOptions, 0, 1, 1, 2, Gtk::FILL, Gtk::FILL);
			$tbl->attach($includeCheckBox, 1, 2, 1, 2);
			$tbl->attach($usersInGroup, 0, 2, 2, 3);
			$tbl->attach($groupButtons, 0, 2, 3, 4, Gtk::FILL, Gtk::FILL);

			$filtersWindow->add($tbl);
			$filtersWindow->show_all();
		}
	}

	function groupListChanged($groupList, $filtersWindow)
	{
		global $curGroupName;
		$curGroupName = $groupList->get_active_text();
		$filtersWindow->destroy();
		showFiltersWindow();
	}

	function newListClicked($filtersWindow)
	{
		$filtersWindow->destroy();
		showNewFilterWindow();
	}

	function addUserToGroup($userEntry, $groupName, $usersBox)
	{
		global $groups;

		$userName = strtolower($userEntry->get_text());
		$userEntry->set_text('');
		$groups[$groupName][] = $userName;
		$userHBox = new GtkHBox(true);
		$userLabel = new GtkLabel($userName);
		$userDel = new GtkButton('Delete');
		$userDel->connect_simple('clicked', 'removeUserFromGroup', $userName, $groupName, $userHBox);
		$userHBox->pack_start($userLabel);
		$userHBox->pack_start($userDel);
		$usersBox->pack_start($userHBox);
		$usersBox->show_all();
		saveFilters();
	}

	function delListClicked($groupName, $filtersWindow)
	{
		global $groups, $curGroupName;
		unset($groups[$groupName]);
		$curGroupName = '';
		$filtersWindow->destroy();
		showFiltersWindow();
	}

	function removeUserFromGroup($user, $groupName, $userHBox)
	{
		global $groups;

		$userHBox->destroy();
		reset($groups);
		foreach ( $groups[$groupName] as $key => $value )
		{
			if ( $value == "$user" )
			{
				unset($groups[$groupName][$key]);
				return true;
			}
		}
		return true;
	}

	function changeGroupType($includeCheckBox, $curGroupName)
	{
		global $groups;

		if ( $includeCheckBox->get_active() )
			$groups[$curGroupName]['type'] = 'include';
		else
			$groups[$curGroupName]['type'] = 'exclude';
		saveFilters();
	}

	function showInfoWindow($text)
	{
		global $phpitterIcon;

		$infoWindow = new GtkWindow();
		$infoWindow->set_icon($phpitterIcon);
		$infoWindow->set_default_size(700, 300);
		$scrolledWindow = new GtkScrolledWindow();
		$scrolledWindow->set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
		$scrolledWindow->set_shadow_type(Gtk::SHADOW_IN);

		$textView = new GtkTextView();
		$textView->get_buffer()->set_text($text);
		$textView->set_editable(false);
		$textView->set_cursor_visible(false);

		$scrolledWindow->add_with_viewport($textView);
		$infoWindow->add($scrolledWindow);
		$infoWindow->set_position(Gtk::WIN_POS_MOUSE);
		$infoWindow->show_all();
	}

	function showNewFilterWindow()
	{
		global $phpitterIcon;

		$filtersWindow = new GtkWindow();
		$filtersWindow->set_icon($phpitterIcon);
		$filtersWindow->set_position(Gtk::WIN_POS_MOUSE);
		$newFilterLabel = new GtkLabel('Create a new group');
		$lblFilterName = new GtkLabel('Group name');
		$txtFilterName = new GtkEntry();
		$btnFilterCreate = new GtkButton('Create');

		$tbl = new GtkTable(2, 2);
		$tbl->attach($newFilterLabel, 0, 2, 0, 1);
		$tbl->attach($lblFilterName, 0, 1, 1, 2);
		$tbl->attach($txtFilterName, 1, 2, 1, 2);
		$bbox = new GtkHButtonBox();
		$bbox->add($btnFilterCreate);
		$vbox = new GtkVBox();
		$vbox->pack_start($tbl);
		$vbox->pack_start($bbox);
		$filtersWindow->add($vbox);

		$btnFilterCreate->connect_simple('clicked', 'createFilter', $filtersWindow, $txtFilterName);
		$filtersWindow->show_all();
	}

	function createFilter($filtersWindow, $txtFilterName)
	{
		global $groups, $curGroupName, $listStore;
		$filtersWindow->destroy();

		if ( ! in_array($txtFilterName->get_text(), $groups) )
			$groups[$txtFilterName->get_text()]['type'] = 'include';

		$curGroupName = $txtFilterName->get_text();
		$listStore->append(array($txtFilterName->get_text()));

		showFiltersWindow();
	}

	function loadTheme()
	{
		global $theme, $templates, $baseDir;

		if ( ! is_dir($baseDir.'themes/' . $theme) OR $theme == '' )
			$theme = 'default';

		$themeDir = $baseDir.'themes/' . $theme;

		$templates['tweet_normal'] = file_get_contents($themeDir . '/tweet_normal.html');
		$templates['tweet_reply'] = file_get_contents($themeDir . '/tweet_reply.html');
		$templates['tweet_direct'] = file_get_contents($themeDir . '/tweet_direct.html');
	}

	function updateFriendsTimeline($friendsTable, $pluggable)
	{
		global $tweetsToShow, $phpTwitter, $objectReqs, $templates, $baseDir;

		switch ($tweetsToShow)
		{
			case 'All':
				$timeline = $phpTwitter->getAll();
				krsort($timeline, SORT_NUMERIC);
				break;
			case 'Replies':
				$timeline = $phpTwitter->replies;
				krsort($timeline, SORT_NUMERIC);
				break;
			case 'Directs':
				$timeline = $phpTwitter->directMessagesSent + $phpTwitter->directMessages;
				krsort($timeline, SORT_NUMERIC);
				break;
			default:
				$timeline = filterTweets($tweetsToShow, $phpTwitter->getAll());
				break;
		}

		$currentChildren = $friendsTable->get_children();

		$friendsTable->hide();
		if ( count($currentChildren ) > 0 )
		{
			foreach ( $currentChildren as $childObject )
			{
				while (Gtk::events_pending())
					Gtk::main_iteration_do();

				if ( $childObject->gtype == GtkFrame::gtype)
					$childObject->destroy();
			}
		}

		$row = 0;
		foreach ( $timeline as $tweet )
		{
			while (Gtk::events_pending())
				Gtk::main_iteration();
			if ( trim($tweet['text']) != '' )
			{
				$uImageBox = GtkImage::new_from_file($baseDir.$tweet['user']['local_image']);
				$uImageBox->set_name($row);
				$uEventBox = new GtkEventBox();
				$uEventBox->add($uImageBox);
				$uEventBox->connect_simple('enter-notify-event', 'imageEnter', $uImageBox);
				$uEventBox->connect_simple('leave-notify-event', 'imageLeave', $uImageBox, $tweet['user']['local_image']);
				$uEventBox->connect_simple('button-press-event', 'showTweetDetails', $tweet, $pluggable);

				$aTweetBox = new GtkTextView(); 
				$aTweetBox->set_left_margin(5);
				$tweetText = $pluggable->displayTweet(html_entity_decode(trim($tweet['text'])));
				if ( $tweet['truncated'] == 'true' )
					$tweetText .= '...';

				$aTweetBox->set_wrap_mode(Gtk::WRAP_WORD_CHAR);
				$aTweetBox->set_size_request(10, -1);
				$aTweetBox->set_editable(false);
				$aTweetBox->set_cursor_visible(false);
				$uEventBox->set_border_width(0);
				$emptyEventBox = new GtkEventBox();

				$colors['normal'] = '#ffffff';
				$colors['reply'] = '#c7f9c7';
				$colors['direct'] = '#fcfcad';

				if ( $tweet['type'] == 'direct_message' ) {
					$aTweetBox->modify_base(Gtk::STATE_NORMAL, GdkColor::parse($colors['direct']));
					$uEventBox->modify_bg(Gtk::STATE_NORMAL, GdkColor::parse($colors['direct']));
					$emptyEventBox->modify_bg(Gtk::STATE_NORMAL, GdkColor::parse($colors['direct']));
					$emptyEventBox2 = new GtkEventBox();
					$emptyEventBox2->modify_bg(Gtk::STATE_NORMAL, GdkColor::parse($colors['direct']));

					$aVBox = new GtkVBox();
					$aVBox->pack_start($uEventBox, false, false);
					$aVBox->pack_start($emptyEventBox);

					$rImageBox = GtkImage::new_from_file($tweet['recipient']['local_image']);
					$rImageBox->set_name($row);
					$rEventBox = new GtkEventBox();
					$rEventBox->add($rImageBox);
					$rEventBox->modify_bg(Gtk::STATE_NORMAL, GdkColor::parse($colors['direct']));

					$rVBox = new GtkVBox();
					$rVBox->pack_start($emptyEventBox2);
					$rVBox->pack_start($rEventBox, false, false);

					$aHBox = new GtkHBox();
					$aHBox->pack_start($aVBox, false, false);
					$aHBox->pack_start($aTweetBox);
					$aHBox->pack_start($rVBox, false, false);
					$tweetText .= "\n";
					$endOfTweet = strlen($tweetText);
					$tweetText .= $tweet['user']['name'] . ' to ' . $tweet['recipient']['name'] . ' ' . getFuzzyTime($tweet['created_at']);
				} else {

					if ( strstr(strtolower($tweet['text']), '@' . strtolower($phpTwitter->username)) )
						$type = 'reply';
					else
						$type = 'normal';

					$aTweetBox->modify_base(Gtk::STATE_NORMAL, GdkColor::parse($colors[$type]));
					$uEventBox->modify_bg(Gtk::STATE_NORMAL, GdkColor::parse($colors[$type]));
					$emptyEventBox->modify_bg(Gtk::STATE_NORMAL, GdkColor::parse($colors[$type]));

					$aVBox = new GtkVBox();
					$aVBox->pack_start($uEventBox, false, false);
					$aVBox->pack_start($emptyEventBox);

					$aHBox = new GtkHBox();
					$aHBox->pack_start($aVBox, false, false);
					$aHBox->pack_start($aTweetBox);
					$tweetText .= "\n";
					$endOfTweet = strlen($tweetText);
					$tweetText .= $tweet['user']['name'] . ' from ' . strip_tags(html_entity_decode($tweet['source'])) . ' ' . getFuzzyTime($tweet['created_at']);
				}


				$aTweetBox->get_buffer()->set_text(html_entity_decode($tweetText));

				linkifyTextView($aTweetBox);
				linkifyTwitterLinks($aTweetBox);

				$tagTable = $aTweetBox->get_buffer()->get_tag_table();
				$tagSuffix = new GtkTextTag();
				$tagSuffix->set_property('foreground', '#7c7c7c');
				$tagSuffix->set_property('font-desc', new PangoFontDescription('Arial 8'));
				$tagTable->add($tagSuffix);
				$aTweetBox->get_buffer()->apply_tag($tagSuffix, $aTweetBox->get_buffer()->get_iter_at_offset($endOfTweet), $aTweetBox->get_buffer()->get_end_iter());

				$aTweetFrame = new GtkFrame();
				$aTweetFrame->add($aHBox);

				$friendsTable->attach($aTweetFrame, 0, 1, $row, $row + 1);
				$row++;
			}
		}
		$friendsTable->show_all();
	}

	function linkifyTextView($textView)
	{
		$textView->connect('motion-notify-event', 'textviewHover');
		$buffer = $textView->get_buffer();
		$buffer->connect('mark_set', 'textviewClick');
		$text = $buffer->get_text($buffer->get_start_iter(), $buffer->get_end_iter());
		
		$tagTable = $buffer->get_tag_table();

		$links = array();
		preg_match_all('|[\w]+://[\S]*[a-z0-9]|i', $text  , $links);

		$links = $links[0];

		foreach($links as $link)
		{
			$length = strlen($link);
			$position = strpos($text, $link);

			$tagURL = new GtkTextTag();
			$tagURL->set_property('foreground', '#0000ff');
			$tagURL->url = $link;
			$tagTable->add($tagURL);

			$buffer->apply_tag($tagURL, $buffer->get_iter_at_offset($position), $buffer->get_iter_at_offset($position + $length));
		}
	}

	function linkifyTwitterLinks($textView)
	{
		$buffer = $textView->get_buffer();
		$text = $buffer->get_text($buffer->get_start_iter(), $buffer->get_end_iter());
		
		$tagTable = $buffer->get_tag_table();

		$links = array();
		preg_match_all('|@[a-z0-9_]+|i', $text, $links);

		$links = $links[0];

		foreach($links as $link)
		{
			$length = strlen($link);
			$position = strpos($text, $link) + 1;

			$tagURL = new GtkTextTag();
			$tagURL->set_property('foreground', '#0000ff');
			$tagURL->url = 'http://twitter.com/' . $link;
			$tagTable->add($tagURL);

			$buffer->apply_tag($tagURL, $buffer->get_iter_at_offset($position), $buffer->get_iter_at_offset($position + $length));
		}
	}

	function textviewHover($textView, $event)
	{
		$buffer_location = $textView->window_to_buffer_coords(Gtk::TEXT_WINDOW_TEXT, $event->x, $event->y);
		$iter = $textView->get_iter_at_location($buffer_location[0], $buffer_location[1]);
		if ( $iter == null )
		{
			setCursor('normal', $textView);
			clearUpdateLabel();
			return;
		}

		$tags = array();
		$tags = $iter->get_tags();

		if ( count($tags) > 0 )
		{
			if ( $tags[0]->url != '' )
			{
				linkHover($tags[0]->url);
				setCursor('link', $textView);
				return true;
			} else {
				setCursor('normal', $textView);
				clearUpdateLabel();
			}
		} else {
			setCursor('normal', $textView);
			clearUpdateLabel();
		}

		return;
	}

	function setCursor($type, $textView)
	{
		global $currentCursor;

		if ( $type == 'link' )
		{
			if ( $currentCursor != 'link' )
			{
				$textView->get_window(Gtk::TEXT_WINDOW_TEXT)->set_cursor(new GdkCursor(Gdk::HAND2));
			}
		} elseif ( $type == 'normal' ) {
			if ( $currentCursor != 'normal' )
			{
				$textView->get_window(Gtk::TEXT_WINDOW_TEXT)->set_cursor(new GdkCursor(Gdk::XTERM));
			}
		}

		$currentCursor = $type;
	}

	function textviewClick($buffer, $textiter, $textmark)
	{
		if ($textmark->get_name()!='insert') return;

		$cursor_pos = $buffer->get_insert();
		$iter = $buffer->get_iter_at_mark($cursor_pos);

		$tags = array();
		$tags = $iter->get_tags();

		if ( count($tags) > 0 )
		{
			$url = $tags[0]->url;

			if ( trim($url) != '' )
			{
				if (PHP_SHLIB_SUFFIX == 'dll')
				{
					$shell = new COM('WScript.Shell');
					$shell->Run('cmd /c start "" "' . $url . '"', 0, FALSE);
					unset($shell);
				} else {
					exec("firefox $url > /dev/null 2>&1 &");
				}
			}
		}

		return true;
	}

	function getFuzzyTime($tweetTime)
	{
		$ago = mktime() - strtotime($tweetTime);
		if ( $ago <= 10 )
		{
			$ago = 'just now';
		} elseif ( $ago < 60 ) {
			$agoTime = $ago;
			$ago = "about $agoTime second";
			if ( $agoTime > 1 )
				$ago .= "s";
			$ago .= " ago";
		} elseif ( $ago < 3600 ) {
			$agoTime = intval($ago / 60);
			$ago = "about $agoTime minute";
			if ( $agoTime > 1 )
				$ago .= "s";
			$ago .= " ago";
		} elseif ( $ago < 86400 ) {
			$agoTime = intval($ago / 3600);
			$ago = "about $agoTime hour";
			if ( $agoTime > 1 )
				$ago .= "s";
			$ago .= " ago";
		} else {
			$ago = 'at ' . date('n/j/Y h:i:s A', strtotime($tweetTime));
		}

		return $ago;
	}

	function login($loginWindow, $phpitterWindow, $friendsTable, $username, $password, $pluggable)
	{
		global $phpTwitter, $tweetHistory, $listStore, $groups, $lastStatusLabel;
		$loginWindow->hide_all();

		$phpTwitter = new phpTwitter($username->get_text(), $password->get_text());
		$phpTwitter->maxFriendsUpdatesHistory = $tweetHistory;
		$phpTwitter->loadTweets();
		$phpTwitter->offline = false;

		$lastStatusLabel->set_text($phpTwitter->status);
		loadFilters();


		if ( count($groups) > 0 )
			foreach ( $groups as $group => $array )
				$listStore->append(array($group));

		$phpitterWindow->show_all();

		if  ( count($phpTwitter->friendsTimeline) == 0 )
		{
			checkForUpdates($friendsTable, $pluggable, true);
		}

		updateFriendsTimeline($friendsTable, $pluggable);
		Gtk::timeout_add(4000, 'checkForUpdates', $friendsTable, $pluggable);
		Gtk::timeout_add(5000, 'clearUpdateLabel');
	}

	function sendUpdate($textEntry)
	{
		global $phpTwitter, $friendsTable, $pluggable;

		$buffer = $textEntry->get_buffer();
		$tweet = $buffer->get_text($buffer->get_start_iter(), $buffer->get_end_iter());
		$buffer->set_text('');
		$phpTwitter->sendUpdate($tweet);
		updateStatus('Tweet sent');
		Gtk::timeout_add(5000, 'clearUpdateLabel');
		updateFriendsTimeline($friendsTable, $pluggable);
	}

	function ifEnterSendUpdate($textEntry, $event)
	{
		if ($event->keyval==Gdk::KEY_Return)
		{
			sendUpdate($textEntry);
			return true;
		}
		return false;
	}

	function ifEnterLogin($textEntry, $event, $loginWindow, $phpitterWindow, $friendsTable, $txtUsername, $txtPassword, $pluggable)
	{
		if ($event->keyval==Gdk::KEY_Return)
		{
			login($loginWindow, $phpitterWindow, $friendsTable, $txtUsername, $txtPassword, $pluggable);
			return true;
		}
		return false;
	}

	function checkForUpdates($friendsTable, $pluggable, $force = false)
	{
		global $phpTwitter, $phpitterWindow, $updateLabel, $lastStatusLabel;

		if ( $force )
		{
			$phpTwitter->friendsTimelineLastPull = 0;
			$phpTwitter->directMessagesLastPull = 0;
		}

		phpitterMove($phpitterWindow);

		if ( ( mktime() - $phpTwitter->friendsTimelineLastPull)  > 240 )
		{
			$newUpdates = $phpTwitter->getFriendsTimeline('nonBlockCallback');
			$phpTwitter->saveTweets();
			if ( count($newUpdates) > 0 )
				$pluggable->newUpdates($newUpdates);
			$updateLabel->set_text('');
			$updateLabel->locked = false;
			updateFriendsTimeline($friendsTable, $pluggable);
			$lastStatusLabel->set_text($phpTwitter->status);
		} elseif ( ( mktime() - $phpTwitter->directMessagesLastPull) > 360 ) {
			$newUpdates = $phpTwitter->getDirects('nonBlockCallback');
			$phpTwitter->saveTweets();
			if ( count($newUpdates) > 0 )
				$pluggable->newUpdates($newUpdates);

			$updateLabel->set_text('');
			$updateLabel->locked = false;
			updateFriendsTimeline($friendsTable, $pluggable);
			$lastStatusLabel->set_text($phpTwitter->status);
		}

		return true;
	}

	function nonBlockCallback()
	{
		global $updateLabel;

		if ( $updateLabel->get_text() != 'Fetching updates...' )
		{
			$updateLabel->set_text('Fetching updates...');
			$updateLabel->locked = true;
		}

		while (Gtk::events_pending())
			Gtk::main_iteration();
	}

	function changedEntryBox($updateBox, $updateSpell, $pluggable)
	{
		$pluggable->changedEntryBox($updateBox);
		if ( is_object($updateSpell) )
			$updateSpell->recheck_all();
	}

	function showTweetDetails($tweetData, $pluggable)
	{
		global $phpitterIcon, $phpTwitter;

		$userDataWindow = new GtkWindow();
		$userDataWindow->set_icon($phpitterIcon);
		$userDataWindow->set_decorated(false);
		$userDataWindow->set_position(Gtk::WIN_POS_MOUSE);
		$userDataTable = new GtkTable(2, 1);

		$userData = new GtkTextView(); 
		$userData->set_wrap_mode(Gtk::WRAP_WORD_CHAR);
		$userData->set_editable(false);
		$userData->set_cursor_visible(false);

		$userDataText = $tweetData['user']['name'] . ' (@' . $tweetData['user']['screen_name'] . ')';

	        if ( trim($tweetData['user']['location']) != '' )
			$userDataText .= ' in ' . $tweetData['user']['location'];
		if ( trim($tweetData['in_reply_to']) != '' )
		{
			$userDataText .= "\n\n";
			$userDataText .= 'Reply to: "' . $pluggable->displayTweet($tweetData['in_reply_to']) . '"';
		}
		if ( trim($tweetData['user']['description']) != '' )
		{
			$userDataText .= "\n\n";
			$userDataText .= $tweetData['user']['description'];
		} else {
			$userDataText .= "\n\n";
			$userDataText .= 'No description';
		}
		if ( trim($tweetData['user']['url']) != '' )
		{
			$userDataText .= "\n\n";
			$userDataText .= 'URL: ' . $tweetData['user']['url'];
		}
		$userDataText .= "\n\n";
		$userDataText .= 'Followers: ' . $tweetData['user']['followers_count'];
		$userData->get_buffer()->set_text(html_entity_decode($userDataText));
		linkifyTextView($userData);
		linkifyTwitterLinks($userData);

		$userData->set_size_request(300, -1);

		$userDataFrame = new GtkFrame();
		$userDataFrame->modify_bg(Gtk::STATE_NORMAL, GdkColor::parse('#000000'));
		$userDataFrame->add($userData);
		$userDataFrame->set_name('userdata');
		$replyButton = new GtkButton('_Reply');
		$replyButton->set_name('userdata');
		$replyButton->connect_simple('enter-notify-event', 'noDontLeave');
		$replyButton->connect_simple('clicked', 'insertUpdateBox', '@' . $tweetData['user']['screen_name'] . ' ');
		$replyButton->connect_simple('clicked', 'hideTweetDetails', $userDataWindow);
		$retweetButton = new GtkButton('_Retweet');
		$retweetButton->set_name('userdata');
		$retweetButton->connect_simple('enter-notify-event', 'noDontLeave');
		$retweetButton->connect_simple('clicked', 'changeUpdateBox', 'Retweeting @' . $tweetData['user']['screen_name'] . ': ' . $tweetData['text']);
		$retweetButton->connect_simple('clicked', 'hideTweetDetails', $userDataWindow);
		$directButton = new GtkButton('_Direct');
		$directButton->set_name('userdata');
		$directButton->connect_simple('enter-notify-event', 'noDontLeave');
		$directButton->connect_simple('clicked', 'changeUpdateBox', 'd ' . $tweetData['user']['screen_name'] . ' ');
		$directButton->connect_simple('clicked', 'hideTweetDetails', $userDataWindow);
		$bbox = new GtkHButtonBox();
		$bbox->set_name('userdata');
		$bbox->add($replyButton);
		$bbox->add($retweetButton);
		$bbox->add($directButton);
		$userDataTable->attach($userDataFrame, 0, 1, 0, 1);
		$userDataTable->attach($bbox, 0, 1, 1, 2, Gtk::FILL);
		$userDataEvent = new GtkEventBox();
		$userDataEvent->set_name('userdata');
		$userDataEvent->add($userDataTable);

		$userDataWindow->add($userDataEvent);
		$userDataWindow->show_all();
		$userDataEvent->connect_simple('leave-notify-event', 'setLeave', $userDataWindow);
	}

	function hideTweetDetails($userDataWindow)
	{
		if ( is_object($userDataWindow) )
			$userDataWindow->destroy();
	}

	function imageEnter($image)
	{
		$imagePixBuf = $image->get_pixbuf();
		$imagePixBuf->saturate_and_pixelate($imagePixBuf, 4, true);
		$image->set_from_pixbuf($imagePixBuf);
	}

	function imageLeave($image, $imageFile)
	{
		$image->set_from_file($imageFile);
	}

	function setLeave($userDataWindow)
	{
		global $userDataTimeout;
		$userDataTimeout = Gtk::timeout_add(100, 'hideTweetDetails', $userDataWindow);
	}

	function noDontLeave()
	{
		global $userDataTimeout;
		if ( is_int($userDataTimeout) )
			$userDataTimeout = Gtk::timeout_remove($userDataTimeout);
	}

	function changeUpdateBox($text)
	{
		global $updateBox;
		$buffer = $updateBox->get_buffer();
		$buffer->delete($buffer->get_start_iter(), $buffer->get_end_iter());
		$buffer->set_text($text);
		$updateBox->grab_focus();
	}

	function insertUpdateBox($text)
	{
		global $updateBox;
		$buffer = $updateBox->get_buffer();
		$buffer->insert_at_cursor($text);
		$updateBox->grab_focus();
	}

	function clearUpdateLabel()
	{
		global $updateLabel;
		if ( ! $updateLabel->locked )
			$updateLabel->set_text('');

		return;
	}

	function listOptionChanged($listOption, $tweetTable, $pluggable)
	{
		while (Gtk::events_pending())
			Gtk::main_iteration_do();
		global $tweetsToShow, $phpTwitter;
		$tweetsToShow = $listOption->get_active_text();
		updateFriendsTimeline($tweetTable, $pluggable);
	}

	function filterTweets($groupName, $allTweets)
	{
		global $groups;

		$group = $groups[$groupName];

		foreach ( $allTweets as $tweet )
		{
			if ( trim($tweet['text']) != '' )
			{
				if ( $group['type'] == 'include' )
				{
					if ( in_array(strtolower($tweet['user']['screen_name']), $group) )
						$filteredTimeline[] = $tweet;
				} else {
					if ( ! in_array(strtolower($tweet['user']['screen_name']), $group) )
						$filteredTimeline[] = $tweet;
				}
			}
		}

		return $filteredTimeline;
	}

	function linkHover($url)
	{
		//updateStatus(htmlentities($url));
	}

	function exitPhpitter()
	{
		saveFilters();

		Gtk::main_quit();
	}

	function saveFilters()
	{
		global $groups, $phpTwitter, $baseDir;

		if ( $phpTwitter->username != '' )
			file_put_contents($baseDir.$phpTwitter->username . ".filters.phpitter", serialize($groups));
	}

	function loadFilters()
	{
		global $groups, $phpTwitter;
		if ( is_readable($phpTwitter->username . ".filters.phpitter") )
		{
			$groups = unserialize(file_get_contents($baseDir.$phpTwitter->username . ".filters.phpitter"));
		} else {
			$groups = array();
		}
	}

	function showHidePhpitter($window)
	{
		if ( $window->visible )
			$window->hide();
		else
			$window->present();
	}

	function phpitterHide($window)
	{
		$window->visible = false;
	}
	
	function phpitterShow($window)
	{
		$window->visible = true;
		$window->deiconify();
		if ( is_array($window->current_position) )
			$window->move($window->current_position[0], $window->current_position[1]);
	}

	function phpitterMove($window)
	{
		$position = $window->get_position();
		if ( $position[0] != 0 AND $position[1] != 0 )
			$window->current_position = $position;
	}

	function visibleWindow($window, $event)
	{
		$state = $event->state;

		if ( $state != 0 )
			$window->visible = false;
		else
			$window->visible = true;
	}

	function showTrayMenu()
	{
		global $phpTwitter, $friendsTable, $pluggable;

		$menu = new GtkMenu();
		$reloadItem = new GtkMenuItem('Fetch Updates');
		$menu->append($reloadItem);
		$reloadItem->connect_simple('activate', 'checkForUpdates', $friendsTable, $pluggable, true);
		$menu->append(new GtkSeparatorMenuItem());
		$exitItem = new GtkMenuItem('Exit');
		$menu->append($exitItem);
		$exitItem->connect('activate', 'exitPhpitter');
		$menu->show_all();
		$menu->popup();
	}

	function sendMapData()
	{
		global $phpTwitter;

		$mapID = $phpTwitter->sendMapData();

		$url = 'http://phpitter.com/map/index.php?id=' . $mapID;

		if ( $mapID != -1 )
		{
			if (PHP_SHLIB_SUFFIX == 'dll')
			{
				$shell = new COM('WScript.Shell');
				$shell->Run('cmd /c start "" "' . $url . '"', 0, FALSE);
				unset($shell);
			} else {
				exec("firefox $url > /dev/null 2>&1 &");
			}
		}
	}

	function dontDelete($phpitterWindow)
	{
		$phpitterWindow->hide();
		return true;
	}

	function phpitterWindowStateEvent($phpitterWindow, $windowStateEvent, $userData)
	{
		if ( $windowStateEvent->new_window_state == 2 )
			$phpitterWindow->visible = false;
		elseif ( $phpitterWindow->visible == false AND $windowStateEvent->new_window_state == 0 )
			$phpitterWindow->visible = true;

		return;
	}

	function updateStatus($message)
	{
		global $updateLabel;

		if ( ! $updateLabel->locked )
			$updateLabel->set_text($message);
	}

	Gtk::main();
?>
